const std = @import("std");
const net = @import("../net.zig");
const log = std.log.scoped(.ether);
const assert = std.debug.assert;
const Mac = net.Mac;

// https://github.com/ziglang/zig/issues/10113#issuecomment-1241791584
/// an Ethernet packet header (start of the packet).
pub const Ether = extern struct {
    dhost: Mac align(1) = net.host_mac,
    shost: Mac align(1) = net.local_mac,
    type: Type align(1),

    pub const Type = enum(u16) {
        /// Internet protocol
        ip = 0x0800,
        /// Address resolution protocol
        arp = 0x0806,
        _,
    };

    comptime {
        assert(@alignOf(@This()) == 1);
        assert(@bitSizeOf(@This()) / 8 == @sizeOf(@This()));
    }

    pub const header_size = @sizeOf(@This());
    pub const packet_lenth = header_size;
};

pub fn send(packet: []u8, t: Ether.Type, dhost: Mac) !void {
    const eth: *Ether = @ptrCast(&packet[0]);
    eth.* = .{
        .dhost = dhost,
        .type = t,
    };
    net.swapAllFields(eth);

    try net.send(packet);
}

pub fn recv(packet: []u8) !void {
    const eth: *Ether = @ptrCast(&packet[0]);
    net.swapAllFields(eth);

    switch (eth.type) {
        .ip => try net.ip.recv(packet, eth),
        .arp => try net.arp.recv(packet, eth),
        _ => log.warn("unknown ether packet: {}", .{@intFromEnum(eth.type)}),
    }
}
